/*
 * Copyright 2008. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the Apache License, Version 2.0. You
 * can find a copy of the license at:
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */

package com.sinai.mshab.format;

import java.util.HashMap;

import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.format.util.Observable;
import com.sinai.mshab.format.util.Observer;

/**
 * Creates a panel that can accept formatting of it's widgets. Supports default
 * formatting, use .setDefault....().
 * 
 * You can add descriptive labels to the widgets you add, you can format those
 * labels. You can add regex validators, required fields, and custom widgets all
 * around the widget being added.
 * 
 * You can specify fancy at-least-one-must-not-be-blank requirements on any
 * number of widgets.
 * 
 * @author serge
 */
public class FormatPanel extends VerticalPanel implements Observer {

	// all defaults of this panel are stored in this structure.
	private FormatPanelDefaults defaults;

	// this stores relationship of widget added to their container.
	private HashMap<Widget, FormattingContainer> widgetToContainer = new HashMap<Widget, FormattingContainer>();

	/**
	 * Creates a new format panel.
	 */
	public FormatPanel() {

		// set up the panel.
		setStyleName("mshab-formatPanel");
		setSpacing(5);
		defaults = new FormatPanelDefaults();
	}

	/**
	 * Adds a widget to the format panel.
	 */
	public void add(Widget w) {

		// create a container
		FormattingContainer con = new FormattingContainer(defaults, w);

		// register the widget with the container.
		widgetToContainer.put(w, con);

		// Add focus, click, and key listeners where possible.
		if (w instanceof FocusWidget) {
			((FocusWidget) w).addFocusListener(new FocusListener() {

				public void onFocus(Widget sender) {
					sender.addStyleDependentName("focus");
				}

				public void onLostFocus(Widget sender) {
					sender.removeStyleDependentName("focus");
				}
			});
		}

		// adding the container.
		super.add(con);
	}

	/**
	 * Removes a widget and it's container from the panel. Everything associated
	 * with this widget, it's labels, decoration etc, will be removed.
	 * 
	 * @param w
	 *            the widget to remove.
	 */
	public boolean remove(Widget w) {
		FormattingContainer cont = widgetToContainer.get(w);
		if (cont != null) {
			return super.remove(cont);
		}
		return false;
	}

	/**
	 * This will lightly indent the container of the given widget. How much the
	 * intentation is can be controlled by CSS using ".mshab-container-indent"
	 * 
	 * @param w
	 *            the widget to indent.
	 */
	public void indent(Widget w) {
		FormattingContainer cont = widgetToContainer.get(w);
		cont.setStyleName("mshab-container-indent");
	}

	/**
	 * Adds a line of whitespace for formatting purposes
	 */
	public void addBreak() {
		Label br = new Label("");
		br.setStyleName("mshab-break");
		add(br);
	}

	/**
	 * Creates a label with the given title for the specified widget.
	 * 
	 * @param widget
	 *            the widget who's title to set.
	 * @param title
	 *            the title to set.
	 */
	public void setLabel(Widget widget, String title) {
		FormattingContainer cont = widgetToContainer.get(widget);
		cont.setLabel(title);
	}

	/**
	 * Creates a label with the given title for the speficied widget in a given
	 * direction
	 * 
	 * @param widget
	 *            the widget who's title to set.
	 * @param title
	 *            the title to set.
	 * @param dir
	 *            the orientation of the title with respect to the widget.
	 */
	public void setLabel(Widget widget, String title, Direction dir) {
		FormattingContainer cont = widgetToContainer.get(widget);
		cont.addLabel(title, dir);
	}

	/**
	 * This method is similar to setLabel() though it takes in a widget instead
	 * of a string for a label. Furthermore, several widgets can be added in the
	 * same directional slot.
	 * 
	 * @param widget
	 *            the main widget to which to add a surrounding widget
	 * @param decorator
	 *            the surrounding widget.
	 * @param direction
	 *            the orientation of the surrounding widget with respect to
	 *            original widget
	 */
	public void addDecoration(Widget w, Widget decorator, Direction direction) {
		FormattingContainer cont = widgetToContainer.get(w);
		cont.addWidget(decorator, direction);
	}

	/**
	 * Run validation on all the registered widgets to make sure all required
	 * fields are filled and everything that has a regex attached to it does in
	 * fact validate well.
	 * 
	 * This will trigger any widgets's notifiers which are not ok.
	 * 
	 * @return true if everything is ok, false if there is at least one widget
	 *         that is required and not filled, or does not pass regex
	 *         validation
	 */
	public boolean validate() {
		return false;
	}

	/**
	 * Hides the container of the associated widget.
	 * 
	 * @param widget
	 *            the widget which is to be hidden
	 */
	public void hide(Widget widget) {
		// get the container associated with widget.
		FormattingContainer cont = widgetToContainer.get(widget);
		cont.setVisible(false);
	}

	/**
	 * Shows the given widget if it is hidden
	 * 
	 * @param widget
	 *            the widget to be shown.
	 */
	public void show(Widget widget) {
		// get the container associated with widget.
		FormattingContainer cont = widgetToContainer.get(widget);
		cont.setVisible(true);
	}

	/**
	 * Creates a show rule based on another widget being set.
	 * 
	 * @param original
	 *            the widget to show
	 * @param conditional
	 *            the widget that must be set.
	 */
	public void showIfSet(Widget original, Widget conditional) {
		// get the container associated with widget.
		FormattingContainer cont = widgetToContainer.get(original);
		VisibilityManager.getInstance().addShowRule(cont, conditional);
	}

	/**
	 * Creates a hide rule based on another widget being set.
	 * 
	 * @param original
	 *            the widget to hide
	 * @param conditional
	 *            the widget that must be set.
	 */
	public void hideIfSet(Widget original, Widget conditional) {
		// get the container associated with widget.
		FormattingContainer cont = widgetToContainer.get(original);
		VisibilityManager.getInstance().addHideRule(cont, conditional);
	}

	/**
	 * Creates a show/hide rule based on a value of a widget.
	 * 
	 * @param original
	 *            the widget to show/hide
	 * @param conditional
	 *            the widget who's value determines visibility
	 * @param regex
	 *            the condition must be met to show, else original widget is
	 *            hiden.
	 */
	public void showHideIfValue(Widget original, Widget conditional,
			String regex) {

		// get the container associated with widget.
		FormattingContainer cont = widgetToContainer.get(original);
		VisibilityManager.getInstance().addValueRule(cont, conditional, regex);
	}

	/* Boring setters and getters */

	public void setDefaultLabelPosition(Direction defaultLabelPosition) {
		defaults.setLabelPosition(defaultLabelPosition);
	}

	public void setDefaultLeftLabelWidth(int defaultLeftLabelWidth) {
		defaults.setLeftLabelWidth(defaultLeftLabelWidth);
	}

	public void update(Observable o, Object arg) {
		// TODO Auto-generated method stub

	}
}
